﻿//---------------------------------------------------------------------
//  Copyright (c) 2012 Daun Project.  All rights reserved.
//  http://daun.codeplex.com
//  http://www.daun-project.com
//---------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using MongoDB.Driver;

//using Daun.Cores.Entities;
namespace Daun.Cores
{
    public class MongoEngine
    {
        private MongoServer _server = null;
        private string _specificDatabase = String.Empty;
        public string ErrorMessage { set; get; }
        public string ServerName { set; get; }
        public string UniqueName { set; get; }
        public Guid EngineId { set; get; }

        private MongoEngine()
        {
        }
        
        public static MongoEngine Connect(string host, int port, string userName, string password, string database, ref string errorMessage)
        {
            if(port<=0)
                port = 27017;
            string conString = string.Empty;
            if (!string.IsNullOrEmpty(host) && !string.IsNullOrEmpty(userName))
            {
                if(string.IsNullOrEmpty(database))
                    conString = String.Format("mongodb://{0}(admin):{1}@{2}:{3}", userName, password, host, port); 
                else
                    conString = String.Format("mongodb://{0}:{1}@{2}:{3}/{4}", userName, password, host, port,database); 
            }
            else
                if (!string.IsNullOrEmpty(host))
                {
                    if (string.IsNullOrEmpty(database))
                        conString = String.Format("mongodb://{0}:{1}", host, port);
                    else
                        conString = String.Format("mongodb://{0}:{1}/{2}", host, port, database);
                }
            if (string.IsNullOrEmpty(conString))
            {
                errorMessage = "Invalid connection parameter";
                return null;
            }
            
            try
            {
                var client = new MongoClient(conString);
                MongoEngine engine = new MongoEngine { _server = client.GetServer() };                
                engine._server.Connect();
                engine.ServerName = host;
                engine.UniqueName = string.Format("{0}@{1}", userName, host);
                engine.EngineId = Guid.NewGuid();
                engine._specificDatabase = database;
                return engine;
            }
            catch (Exception err)
            {
                errorMessage = err.Message;
                return null;
            }           
        }

        public static bool TestConnection(string host, int port, string userName, string password,string database, ref string errorMessage)
        {
            MongoEngine engine = MongoEngine.Connect(host, port, userName, password,database, ref errorMessage);
            if (engine != null)
            {
                engine.Disconnect();
                return true;
            }

            return false;
        }

        public bool Disconnect()
        {
            if (_server == null)
                return false;

            _server.Disconnect();
            return true;
        }
        public List<string> GetDatabaseList()
        {
            if (_server == null)
                return null;

            if(!string.IsNullOrEmpty(_specificDatabase))
            {
                var list = new List<String>();
                list.Add(_specificDatabase);

                return list;
            }
            
            return new List<string>(_server.GetDatabaseNames());            
        }

        public List<string> GetCollectionList(string databaseName)
        {
            if (_server == null)
                return null;

            MongoDatabase db = _server.GetDatabase(databaseName);
            

            return new List<string>(db.GetCollectionNames());
        }
        public Dictionary<int,List<Daun.Cores.Entities.Document>> GetCollectionDataList(string databaseName,string collectionName)
        {
            if (_server == null)
                return null;

            MongoDatabase db = _server.GetDatabase(databaseName);
            
            
            MongoCollection<MongoDB.Bson.BsonDocument> bsonDocs = db.GetCollection(collectionName);            
            MongoCursor<MongoDB.Bson.BsonDocument> cursors = bsonDocs.FindAll();

            Dictionary<int, List<Daun.Cores.Entities.Document>> list = new Dictionary<int,List<Daun.Cores.Entities.Document>>();
            int counter = 0;
            foreach (var bson in cursors.ToList())
            {                
                List<Daun.Cores.Entities.Document> items = new List<Entities.Document>();
                foreach (var element in bson.Elements)
                {
                    Daun.Cores.Entities.Document doc = new Entities.Document();
                    doc.Key = element.Name;
                    doc.Value = string.Format("{0}", element.Value);
                    if (element.Value.BsonType == MongoDB.Bson.BsonType.ObjectId)
                        doc.DocumentType = "ObjectId";
                    else
                        doc.DocumentType = element.Value.BsonType.ToString();

                    items.Add(doc);                    
                }
                list.Add(counter,items);
                counter++;
            }

            return list;
        }

        public List<Daun.Cores.Entities.Document> GetCollectionScheme(string databaseName, string collectionName)
        {
            if (_server == null)
                return null;

            MongoDatabase db = _server.GetDatabase(databaseName);

            MongoCollection<MongoDB.Bson.BsonDocument> bsonDocs = db.GetCollection(collectionName);
           
            MongoDB.Bson.BsonDocument doc = bsonDocs.FindOne();

            List<Daun.Cores.Entities.Document> list = new List<Daun.Cores.Entities.Document>();
            foreach (var element in doc.Elements)
            {
                Daun.Cores.Entities.Document item = new Entities.Document(element.Name,"","");
                if (element.Value.BsonType == MongoDB.Bson.BsonType.ObjectId)
                    item.DocumentType = "ObjectId";
                else
                    item.DocumentType = element.Value.BsonType.ToString();

                list.Add(item);
            }
            
            return list;
        }

        public List<string> GetUserList(string databaseName)
        {
            if (_server == null)
                return null;

            List<string> list = new List<string>();

            MongoDatabase db = _server.GetDatabase(databaseName);            
            MongoUser[] users = db.FindAllUsers();
            foreach (var user in users)
                list.Add(user.Username);

            return list;
        }
        public bool InsertNewUser(string databaseName,string userName,string password,bool isReadonly)
        {
            if (_server == null)
                return false;

            MongoDatabase db = _server.GetDatabase(databaseName);
            string hashPassword = MongoUser.HashPassword(userName, password);
            MongoUser user = new MongoUser(userName, hashPassword, isReadonly);
            db.AddUser(user);
            
            return true;
        }
        public bool DeleteUser(string databaseName, string userName)
        {
            if (_server == null)
                return false;

            MongoDatabase db = _server.GetDatabase(databaseName);
            db.RemoveUser(userName);
           
            return true;
        }
        public bool InsertNewDocument(string databaseName, string collectionName, List<Entities.Document> doc)
        {
            if (_server == null)
                return false;

            MongoDatabase db = _server.GetDatabase(databaseName);
            MongoCollection<MongoDB.Bson.BsonDocument> bsonDocs = db.GetCollection(collectionName);      
            
            MongoDB.Bson.BsonDocument item = new MongoDB.Bson.BsonDocument();
            foreach (var field in doc)
            {
                if (field.Key == "_id" && field.DocumentType == "ObjectId")
                    continue;
                item.Add(field.Key, MongoHelpers.GetBsonValue(field.DocumentType, field.Value));
            }

            WriteConcernResult result = bsonDocs.Insert(item);
            if (result.Ok)
                return true;
            
            return false;
        }

        public bool UpdateDocument(string databaseName, string collectionName,List<Daun.Cores.Entities.Document> old, List<Daun.Cores.Entities.Document> doc)
        {
            if (_server == null)
                return false;

            MongoDatabase db = _server.GetDatabase(databaseName);
            
            MongoCollection<MongoDB.Bson.BsonDocument> bsonDocs = db.GetCollection(collectionName);
            string ids = string.Empty;
            var idDoc = from a in old where a.Key == "_id" && a.DocumentType == "ObjectId" select a;            
            
            if (idDoc.Count() > 0)
                ids = idDoc.ToArray()[0].Value;
            if (!string.IsNullOrEmpty(ids))
            {    
                MongoDB.Bson.BsonValue id = MongoDB.Bson.BsonObjectId.Parse(ids);
                MongoDB.Bson.BsonDocument bsonDoc = bsonDocs.FindOneById(id);

                if (bsonDoc != null)
                {
                    foreach (var item in doc)
                    {
                        if (item.Key != "_id")
                            bsonDoc[item.Key] = MongoHelpers.GetBsonValue(item.DocumentType, item.Value);
                    }
                    WriteConcernResult result = bsonDocs.Save(bsonDoc);

                    if (result.Ok)
                        return true;
                }
            }
            else
            {
                IMongoQuery query = new QueryDocument(); 
                foreach (var item in old)
                {
                    query = MongoDB.Driver.Builders.Query.And(query,
                        MongoDB.Driver.Builders.Query.EQ(item.Key, MongoHelpers.GetBsonValue(item.DocumentType, item.Value))); 
                }
                MongoDB.Bson.BsonDocument bsonDoc = bsonDocs.FindOne(query);
                if (bsonDoc != null)
                {
                    foreach (var item in doc)
                        bsonDoc[item.Key] = MongoHelpers.GetBsonValue(item.DocumentType, item.Value);
                    WriteConcernResult result = bsonDocs.Save(bsonDoc);                   
                    if (result.Ok)
                        return true;
                }
            }
           
            return false;
        }
        public bool DeleteDocument(string databaseName, string collectionName, List<Daun.Cores.Entities.Document> doc)
        {
            if (_server == null)
                return false;

            MongoDatabase db = _server.GetDatabase(databaseName);

            MongoCollection<MongoDB.Bson.BsonDocument> bsonDocs = db.GetCollection(collectionName);
            IMongoQuery query = new QueryDocument();
            foreach (var item in doc)
            {
                query = MongoDB.Driver.Builders.Query.And(query,
                    MongoDB.Driver.Builders.Query.EQ(item.Key, MongoHelpers.GetBsonValue(item.DocumentType, item.Value)));
            }
            
            WriteConcernResult result = bsonDocs.Remove(query);            
            if (result.DocumentsAffected>0)
                return true;

           
            return false;
        }
        public bool DeleteCollection(string databaseName, string collectionName)
        {
            if (_server == null)
                return false;

            MongoDatabase db = _server.GetDatabase(databaseName);
            CommandResult result = db.DropCollection(collectionName);
            if (result.Ok)
                return true;

            return false;
        }
        public bool RenameCollection(string databaseName, string collectionName,string newCollectionName)
        {
            if (_server == null)
                return false;

            MongoDatabase db = _server.GetDatabase(databaseName);
            CommandResult result = db.RenameCollection(collectionName,newCollectionName);
            if (result.Ok)
                return true;

            return false;
        }
        //public List<string> GetGridFSList(string databaseName)
        //{
        //    if (_server == null)
        //        return null;

        //    List<string> list = new List<string>();

        //    MongoDatabase db = _server.GetDatabase(databaseName, SafeMode.True);
        //    MongoCollection<MongoDB.Bson.BsonDocument> docs = db.GridFS.Files;
        //    docs.
        //    foreach (var file in db.GridFS.Files)
        //    {
                
        //    }
        //    MongoUser[] users = db.FindAllUsers();
        //    foreach (var user in users)
        //        list.Add(user.Username);

        //    return list;
        //}
    }
}
